package com.xu.cloud.common.process.support;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StreamUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;
import org.springframework.web.util.WebUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Objects;

/**
 * post请求ServerHttpRequest
 *
 * @author xuguan
 * @since 2025/2/21
 */
@SuppressWarnings("NullableProblems")
public class PostServerHttpRequest extends ServletServerHttpRequest {
    private ObjectMapper objectMapper;

    public PostServerHttpRequest(HttpServletRequest servletRequest) {
        super(servletRequest);
    }

    public ObjectMapper getObjectMapper() {
        return objectMapper;
    }

    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

    @Override
    public InputStream getBody() throws IOException {
        final HttpServletRequest servletRequest = getServletRequest();

        if (!HttpMethod.POST.matches(servletRequest.getMethod())) {
            return servletRequest.getInputStream();
        }

        final String contentType = servletRequest.getContentType();
        if (!StringUtils.hasText(contentType)) {
            return servletRequest.getInputStream();
        }

        if (contentType.toLowerCase().contains("json")) {
            return servletRequest.getInputStream();
        }

        if (contentType.toLowerCase().contains("x-www-form-urlencoded")) {
            Assert.notNull(objectMapper, "objectMapper must not be null");
            final ObjectNode node = objectMapper.createObjectNode();
            final Iterator<String> parameterNames = CollectionUtils.toIterator(servletRequest.getParameterNames());
            while (parameterNames.hasNext()) {
                final String key = parameterNames.next();
                final String value = servletRequest.getParameter(key);
                node.put(key, value);
            }
            final byte[] bytes = objectMapper.writeValueAsBytes(node);
            return new ByteArrayInputStream(bytes);
        }

        if (contentType.toLowerCase().contains("form-data")) {
            final MultipartHttpServletRequest multipartHttpServletRequest = WebUtils.getNativeRequest(servletRequest, MultipartHttpServletRequest.class);
            Assert.notNull(objectMapper, "objectMapper must not be null");
            final ObjectNode node = objectMapper.createObjectNode();
            final Iterator<String> parameterNames = CollectionUtils.toIterator(servletRequest.getParameterNames());
            while (parameterNames.hasNext()) {
                final String partName = parameterNames.next();
                final String paramValue;
                if (Objects.isNull(multipartHttpServletRequest)) {
                    try {
                        paramValue = StreamUtils.copyToString(new StandardMultipartHttpServletRequest(servletRequest).getPart(partName).getInputStream(), FORM_CHARSET);
                    } catch (Exception ex) {
                        throw new MultipartException("Could not parse multipart servlet request", ex);
                    }
                } else {
                    final MultipartFile file = multipartHttpServletRequest.getFile(partName);
                    if (Objects.isNull(file)) {
                        paramValue = multipartHttpServletRequest.getParameter(partName);
                    } else {
                        paramValue = StreamUtils.copyToString(file.getInputStream(), FORM_CHARSET);
                    }
                }
                node.put(partName, paramValue);
            }
            final byte[] bytes = objectMapper.writeValueAsBytes(node);
            return new ByteArrayInputStream(bytes);

        }

        return servletRequest.getInputStream();
    }
}
